<?php
/*
	
	The DynamicRSS PHP Class
	By Cory S.N. LaViska
	Version 1.0, December 07, 2008
	
	Easily create a dynamic RSS 2.0 feed from items in your MySQL database.
	
	
	LICENSE & COPYRIGHT
	
	Copyright (C) 2008 A Beautiful Site, LLC. (http://abeautifulsite.net)
	
	For full documentation, visit http://abeautifulsite.net/notebook/86
	
	This program is free software: you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation, either version 3 of the License, or
	(at your option) any later version.
	
	This program is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.
	
	You should have received a copy of the GNU General Public License
	along with this program.  If not, see <http://www.gnu.org/licenses/>.
	
	RSS 2.0 Documentation
	
		http://cyber.law.harvard.edu/rss/rss.html
	
	
	SAMPLE USAGE
	
		$rss = new DynamicRSS();
		$rss->db('localhost', 'db_name', 'db_user', 'db_password')
			->query('[your_mysql_query_to_fetch_items]')
			->channel( array(
				'title'=>'Example RSS Feed',
				'link'=>'http://example.com/',
				'description'=>'Feed Description',
				'language'=>'en-us',
				'copyright'=>'Copyright 2008 Nobody, Inc.',
				'managingEditor'=>'someone@example.com (FirstName LastName)'
			))
			->item( array(
				'title'=>'[your_mysql_title_field]',
				'link'=>'[your_mysql_link_field]',
				'author'=>'[your_mysql_author_field]',
				'description'=>'[your_mysql_description_field]',
				'guid'=>'[your_mysql_guid_field]', // usually the same as 'link'
				'pubDate'=>'[your_mysql_pubDate_field]'
			))
			->generate();
	
	* Reference database fields in query() and item_xml() methods using {{field_name}}
	
*/

class DynamicRSS {
	
	// Required
	public $db = array(), $feed_url, $indent, $channel, $channel_xml, $item, $item_xml;
	private $feed_data;
	
	function __construct() {
		// Default values
		$this->item = array();
		$this->channel = array();
		$this->indent = '    ';
		
		// Determine self URL (manually override with the feed_url method)
		$url = parse_url(preg_match('/^https/i', $_SERVER['SCRIPT_URI']) ? 'https' : 'http' . "://$_SERVER[HTTP_HOST]$_SERVER[REQUEST_URI]");
		$url = "$url[scheme]://$url[host]$url[path]";
		if( strlen($url['query']) > 1 ) $url .= "?{$url[query]}";
		$this->feed_url = $url;
	}
	
	public function db($host, $name, $user, $pass, $type='mysql') {
		$this->db['host'] = $host;
		$this->db['name'] = $name;
		$this->db['user'] = $user;
		$this->db['pass'] = $pass;
		$this->db['type'] = $type;
		return $this;
	}
	
	public function query($query) {
		$this->db['query'] = $query;
		return $this;
	}
	
	public function feed_url($feed_url) {
		$this->feed_url = $feed_url;
		return $this;
	}
	
	public function indent($indent) {
		$this->indent = (string)$indent;
		return $this;
	}
	
	public function channel($channel = array()) {
		foreach( $channel as $key=>$value ) $this->channel[$key] = $value;
		return $this;
	}
	
	public function channel_xml($channel_xml) {
		$this->channel_xml = $channel_xml;
		return $this;
	}
	
	public function image($url, $width = null, $height = null) {
		// Adjust width/height to default if they exceed the maximum
		$width = $width > 144 ? 88 : intval($width);
		$height = $height > 400 ? 31 : intval($height);
		
		$this->channel['image']['url'] = $url;
		$this->channel['image']['title'] = $this->channel['title'];
		$this->channel['image']['link'] = $this->channel['link'];
		$this->channel['image']['width'] = $width;
		$this->channel['image']['height'] = $height;
		
		return $this;
	}
	
	public function item( $item = array() ) {
		foreach( $item as $key=>$value ) $this->item[$key] = $value;
		return $this;
	}
	
	public function item_xml($item_xml) {
		$this->item_xml = $item_xml;
		return $this;
	}
	
	public function generate($output = 'screen') {
		$this->generate_rss();
		switch( $output ) {
			case 'string':
				return $this->feed_data;
			break;
			case 'screen':
				header("Content-Type: application/rss+xml");
				echo $this->feed_data;
				return $this;
			break;
			default:
				exit('Invalid output mode');
			break;
		}
	}
	
	private function xmlentities($string) {
		// Developer note: need to have a XML compliant to replace this
		return htmlentities($string);
	}
	
	private function generate_rss() {
		
		// Open feed
		$this->feed_data  = "<?xml version = \"1.0\" encoding=\"utf-8\"?>\n";
		$this->feed_data .= "<rss version=\"2.0\">\n";
		$this->feed_data .= "$this->indent<channel>\n";
		//$this->feed_data .= "$this->indent$this->indent<atom:link href=\"" . $this->feed_url . "\" rel=\"self\" type=\"application/rss+xml\" />\n";
		
		// Channel Elements
		foreach( $this->channel as $key=>$value ) {
			if( !is_array($this->channel[$key]) )	{
				// Element
				$this->feed_data .= "$this->indent$this->indent<$key>".$this->xmlentities($value)."</$key>\n";
			} else {
				// Sub-element
				$this->feed_data .= "$this->indent$this->indent<$key>\n";
				foreach( $this->channel[$key] as $subkey=>$subvalue ) $this->feed_data .= "$this->indent$this->indent$this->indent<$subkey>" . $this->xmlentities($subvalue) . "</$subkey>\n";
				$this->feed_data .= "$this->indent$this->indent</$key>\n";
			}
		}
		
		// Raw channel XML
		if( !empty($this->channel_xml) ) $this->feed_data .= "$this->indent$this->indent$this->channel_xml";
		
		// Item
		$d = @mysql_connect($this->db['host'], $this->db['user'], $this->db['pass']);
		if( !$d ) exit('Unable to establish a MySQL connection');
		if( !@mysql_select_db($this->db['name'], $d) ) exit('Unable to connect to database');
		$item = mysql_query($this->db['query']);
		if( !$item ) exit('Failed to execute query');
		
		while( $row = mysql_fetch_array($item) ) {
			
			$this->feed_data .= "$this->indent$this->indent<item>\n";
			foreach( $this->item as $key=>$value ) {
				// Replace {field_name} with the value from the corresponding database field
				preg_match_all('/\{([A-Za-z0-9-_]+)\}/', $row[$key], $m);
				for( $i = 0; $i < count($m); $i++ ) $row[$key] = preg_replace('/\{\{([A-Za-z0-9-_]+)\}\}/', $row[$m[1][$i]], $row[$key]);
				// Add the element
				$this->feed_data .= "$this->indent$this->indent$this->indent<$key>"  .(($key=="description")?str_replace("&","&amp;",$this->xmlentities(html_entity_decode(utf8_decode($row[$value])))):(($key=="title" || $key=="author" || $key=="category")?utf8_decode($row[$value]):$this->xmlentities(utf8_decode($row[$value]))))  . "</$key>\n";
			}
			// Sub-elements
			if( !empty($this->item_xml) ) {
				// Replace {field_name} with the value from the corresponding database field
				preg_match_all('/\{([A-Za-z0-9-_]+)\}/', $this->item_xml, $m);
				for( $i = 0; $i < count($m); $i++ ) $this->item_xml = preg_replace('/\{\{([A-Za-z0-9-_]+)\}\}/', $row[$m[1][$i]], $this->item_xml);
				$this->feed_data .= "$this->indent$this->indent$this->indent$this->item_xml\n";
			}
			$this->feed_data .= "$this->indent$this->indent</item>\n";
			
		}
		
		// Close feed
		$this->feed_data .= "$this->indent</channel>\n";
		$this->feed_data .= "</rss>";
		
        $this->feed_data=utf8_encode($this->feed_data); 
        
		return $this;
		
	}
	
}

?>